package org.dtrd.modules.service.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.dtrd.common.exception.JeecgBootException;
import org.dtrd.common.util.DateUtils;
import org.dtrd.config.system.Constant;
import org.dtrd.config.system.Constant.PackageServiceStatus;
import org.dtrd.config.system.Constant.SignInPlanUsageStatus;
import org.dtrd.modules.achievement.service.IDtrdEntRdSigninPlanService;
import org.dtrd.modules.achievement.service.IDtrdEntRdSigninTemplateService;
import org.dtrd.modules.archive.entity.po.DtrdEntRdArchiveReverseScheme;
import org.dtrd.modules.archive.service.IDtrdEntRdArchiveReverseSchemeService;
import org.dtrd.modules.message.util.MsgEventPusher;
import org.dtrd.modules.patient.entity.bean.report.PatientServiceProgramme;
import org.dtrd.modules.patient.entity.bean.report.ReportFirst;
import org.dtrd.modules.patient.service.IDtrdEntRdReportService;
import org.dtrd.modules.service.entity.bean.PackagePatientListInfo;
import org.dtrd.modules.service.entity.bean.PatientPackageListInfo;
import org.dtrd.modules.service.entity.bean.PatientServiceIndexInfo;
import org.dtrd.modules.service.entity.po.DtrdEntRdServicePackage;
import org.dtrd.modules.service.entity.po.DtrdRlPackagePatient;
import org.dtrd.modules.service.entity.po.DtrdRlPatientServiceDetail;
import org.dtrd.modules.service.entity.po.DtrdRlServicePackage;
import org.dtrd.modules.service.entity.request.PackagePatientPageRequest;
import org.dtrd.modules.service.mapper.DtrdRlPackagePatientMapper;
import org.dtrd.modules.service.service.*;
import org.dtrd.modules.util.ShiroUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Nonnull;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.stream.Collectors;


/**
 * <p>
 * 患者和服务包关系表 服务实现类
 * </p>
 *
 * @author QR
 * @since 2021-12-16
 */
@DS("multi-datasource1")
@Service
@Slf4j
public class DtrdRlPackagePatientServiceImpl extends ServiceImpl<DtrdRlPackagePatientMapper, DtrdRlPackagePatient> implements IDtrdRlPackagePatientService {

    @Autowired
    private IDtrdEntRdServicePackageService packageService;
    @Autowired
    private IDtrdRlServicePackageService servicePackageRlService;
    @Autowired
    private IDtrdRlPackagePatientService packagePatientRlService;
    @Autowired
    private IDtrdRlPatientServiceDetailService patientServiceDetailRlService;
    @Autowired
    private IDtrdEntRdServiceService serviceService;
    @Autowired
    private IDtrdEntRdSigninTemplateService signinTemplateService;
    @Autowired
    private IDtrdEntRdArchiveReverseSchemeService reverseSchemeService;
    @Autowired
    private IDtrdEntRdSigninPlanService signinPlanService;
    @Autowired
    private MsgEventPusher msgEventPusher;
    @Autowired
    private IDtrdEntRdReportService reportService;

    @Override
    public DtrdRlPackagePatient getPackageService(Integer patientId, Integer packageId) {
        LambdaQueryWrapper<DtrdRlPackagePatient> wrapper = baseQueryWrapper()
                .eq(DtrdRlPackagePatient::getPatientId, patientId)
                .eq(DtrdRlPackagePatient::getPackageId, packageId).last(" limit 1 ");
        return getOne(wrapper);
    }

    @Override
    public String getPackageServicePeriod(Integer patientId, Integer packageId) {
        DtrdRlPackagePatient packagePatient = getPackageService(patientId, packageId);
        return Optional.ofNullable(packagePatient).map(p -> DateUtils.combineTwoTime(p.getStartTime(), p.getEndTime())).orElse(null);
    }

    @Override
    public IPage<PackagePatientListInfo> pagePackagePatient(PackagePatientPageRequest request) {
        LambdaQueryWrapper<DtrdRlPackagePatient> queryWrapper = baseQueryWrapper();
        IPage<DtrdRlPackagePatient> page = new Page<>(request.getPageNum(), request.getPageSize());
        page = page(page, queryWrapper);
        return page.convert(PackagePatientListInfo::new);
    }

    @Override
    public boolean hasFinishedServicePackage(Integer patientId) {
        LambdaQueryWrapper<DtrdRlPackagePatient> wrapper = baseQueryWrapper()
                .eq(DtrdRlPackagePatient::getPatientId, patientId)
                .eq(DtrdRlPackagePatient::getStatus, Constant.PackageServiceStatus.FINISHED.getType())
                .last("limit 1");
        DtrdRlPackagePatient patient = getOne(wrapper);
        return patient != null;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean savePatientServiceDetailRelation(Integer patientId, Integer packageId, Integer relationId, Integer periodUnit) {
        // 获取服务包下服务套餐
        List<DtrdRlServicePackage> packageServices = servicePackageRlService.listByPackageId(packageId);
        if (CollectionUtil.isEmpty(packageServices)) {
            throw new JeecgBootException("当前服务包没有项目!");
        }
        // 获取服务项目的 id : name
        Map<Integer, String> serviceIdToName = serviceService.getServiceIdToNameMap();
        if (MapUtil.isEmpty(serviceIdToName)) {
            throw new JeecgBootException("当前没有服务项目!");
        }
        // 生成需要保存的数据 / 假设每个服务项目都有6个月
        List<DtrdRlPatientServiceDetail> details = new ArrayList<>(packageServices.size() * 6);
        packageServices.forEach(servicePackage -> {
            String servicePeriod = servicePackage.getServiceMonth();
            // 可能会有没有周期的服务套餐
            if (StrUtil.isNotBlank(servicePeriod)) {
                // 生成每个周期的记录
                String[] periods = servicePeriod.split(",");
                for (String periodStr : periods) {
                    Integer period = Integer.parseInt(periodStr);
                    Integer serviceId = servicePackage.getServiceId();
                    DtrdRlPatientServiceDetail patientServiceDetail = new DtrdRlPatientServiceDetail(period, patientId, packageId, serviceId, relationId);
                    patientServiceDetail.setPeriodUnit(periodUnit);
                    // 设置当时服务项的名字
                    patientServiceDetail.setServiceName(serviceIdToName.getOrDefault(serviceId, ""));
                    details.add(patientServiceDetail);
                }
            }
        });
        return patientServiceDetailRlService.saveBatch(details);
    }

    @Override
    public Integer savePatientPackageRelation(Integer patientId, Integer packageId, Integer periodUnit, Date startTime, Date endTime) {
        DtrdRlPackagePatient po = new DtrdRlPackagePatient(patientId, packageId, startTime, endTime);
        // 机构编码
        String orgCode = ShiroUtil.getOrgCode();
        po.setOrgCode(orgCode == null ? Constant.DEFAULT_ORGCODE : orgCode);
        // 服务状态
        if (DateUtils.isFuture(startTime)) {
            po.setStatus(Constant.PackageServiceStatus.WAIT_TO_SERVICE.getType());
        } else {
            po.setStatus(Constant.PackageServiceStatus.SERVICING.getType());
        }
        po.setPeriodUnit(periodUnit);
        // 服务关系都做新增处理
        save(po);
        Integer relationId = po.getDataId();
        savePatientServiceDetailRelation(patientId, packageId, relationId, periodUnit);
        return relationId;
    }

    @Override
    public DtrdRlPackagePatient getServicingPatientPackage(Integer patient) {
        List<DtrdRlPackagePatient> list = baseMapper.getServicePackage(patient, PackageServiceStatus.SERVICING.getType());
        return CollectionUtil.isNotEmpty(list) ? list.get(0) : null;
    }

    @Override
    public boolean removePackagePatient(Integer dataId) {
        return removeById(dataId);
    }

    @Override
    public String getPatientInfoStartTime(Integer patientId, Integer status) {
        LambdaQueryWrapper<DtrdRlPackagePatient> wrapper = Wrappers.lambdaQuery(DtrdRlPackagePatient.class)
                .eq(DtrdRlPackagePatient::getPatientId, patientId)
                .eq(DtrdRlPackagePatient::getStatus, status)
                .orderByDesc(DtrdRlPackagePatient::getCreateTime)
                .last("limit 1");
        DtrdRlPackagePatient one = getOne(wrapper);
        return Optional.ofNullable(one)
                .map(p -> DateUtils.combineTwoTime(p.getStartTime(), p.getEndTime(), DateUtils.yyyyMMdd_slash.get().toPattern(), "-"))
                .orElse(null);
    }

    @Override
    public boolean checkPackageRelatePatient(Integer packageId) {
        List<DtrdRlPackagePatient> list = list(baseQueryWrapper()
                .eq(DtrdRlPackagePatient::getPackageId, packageId)
                .eq(DtrdRlPackagePatient::getStatus, Constant.PackageServiceStatus.SERVICING.getType()));
        return CollectionUtil.isNotEmpty(list);
    }

    @Override
    public int countDoctorPatientPackage(Integer doctorId, Integer packageId) {
        return baseMapper.selectDoctorPatientPackageCount(doctorId, packageId);
    }

    @Override
    public DtrdRlPackagePatient getLatestPatientPackage(Integer patientId) {
        LambdaQueryWrapper<DtrdRlPackagePatient> wrapper = baseQueryWrapper()
                .notIn(DtrdRlPackagePatient::getStatus, PackageServiceStatus.WAIT_TO_SERVICE.getType())
                .eq(DtrdRlPackagePatient::getPatientId, patientId)
                .orderByDesc(DtrdRlPackagePatient::getCreateTime)
                .last("limit 1");
        return getOne(wrapper);
    }

    @Override
    public DtrdRlPackagePatient getAllStatusPatientPackage(Integer patientId) {
        LambdaQueryWrapper<DtrdRlPackagePatient> wrapper = baseQueryWrapper()
                .eq(DtrdRlPackagePatient::getPatientId, patientId)
                .orderByDesc(DtrdRlPackagePatient::getCreateTime)
                .last("limit 1");
        return getOne(wrapper);
    }

    @Override
    public PatientServiceIndexInfo getPatientServiceIndexInfo(Integer patientId) {
        LambdaQueryWrapper<DtrdRlPackagePatient> wrapper = Wrappers.lambdaQuery(DtrdRlPackagePatient.class)
                .eq(DtrdRlPackagePatient::getPatientId, patientId)
                .eq(DtrdRlPackagePatient::getStatus, PackageServiceStatus.SERVICING.getType())
                .last("limit 1");
        DtrdRlPackagePatient packagePatientRl = getOne(wrapper);

        // 没有服务中的按开始时间取最新的一条
        if (packagePatientRl == null) {
            LambdaQueryWrapper<DtrdRlPackagePatient> otherWrapper = Wrappers.lambdaQuery(DtrdRlPackagePatient.class)
                    .eq(DtrdRlPackagePatient::getPatientId, patientId)
                    .orderByDesc(DtrdRlPackagePatient::getStartTime)
                    .last("limit 1");
            packagePatientRl = getOne(otherWrapper);
        }
        return Optional.ofNullable(packagePatientRl).map(this::makePatientServiceIndexInfo).orElse(null);
    }

    private PatientServiceIndexInfo makePatientServiceIndexInfo(@Nonnull DtrdRlPackagePatient packagePatientRl) {
        PatientServiceIndexInfo indexInfo = new PatientServiceIndexInfo();
        String packageName = packageService.getPackageName(packagePatientRl.getPackageId());
        indexInfo.setServiceName(packageName);
        indexInfo = indexInfo.parseFromPO(packagePatientRl);
        return indexInfo;
    }

    @Override
    public IPage<PatientPackageListInfo> pagePatientPackage(Integer patientId, Integer page, Integer pageSize, String orgCode, Integer status) {
        LambdaQueryWrapper<DtrdEntRdArchiveReverseScheme> wrapper = Wrappers.lambdaQuery(DtrdEntRdArchiveReverseScheme.class)
                .eq(DtrdEntRdArchiveReverseScheme::getPatientId, patientId)
                .eq(DtrdEntRdArchiveReverseScheme::getIsDel, 0)
                .orderByDesc(DtrdEntRdArchiveReverseScheme::getServiceStartTime)
                .orderByDesc(DtrdEntRdArchiveReverseScheme::getCreateTime);
        IPage<DtrdEntRdArchiveReverseScheme> queryPage = new Page<>(page, pageSize);
        queryPage = reverseSchemeService.page(queryPage, wrapper);
        return queryPage.convert(po -> {
            PatientPackageListInfo info = new PatientPackageListInfo();
            Integer relationId = po.getRelationId();
            info.setPackageId(relationId);
            DtrdRlPackagePatient packagePatient = packagePatientRlService.getById(relationId);
            // 状态
            Integer poStatus = packagePatient.getStatus();
            info.setStatus(poStatus);
            info.setStatusText(Constant.PackageServiceStatus.valueOf(poStatus).getName());
            info.setValidPeriod(DateUtils.combineTwoTime(packagePatient.getStartTime(), packagePatient.getEndTime()));
            // 打卡模版名称
            info.setPlanName(signinTemplateService.getTemplateNameById(po.getSigninTemplateId()));
            // 服务包信息
            Integer packageId = po.getServiceTemplateId();
            if (packageId != null) {
                DtrdEntRdServicePackage servicePackage = packageService.getById(packageId);
                info.setPackageName(servicePackage.getPackageName());
                info.setPackageThumb(servicePackage.getPackageThumb());
            }
            return info;
        });
    }

    @Override
    public boolean isPackageServing(Integer packageId, List<Integer> serviceIds) {
        LambdaQueryWrapper<DtrdRlPackagePatient> wrapper = Wrappers.lambdaQuery(DtrdRlPackagePatient.class)
                .eq(DtrdRlPackagePatient::getPackageId, packageId)
                .eq(DtrdRlPackagePatient::getStatus, Constant.PackageServiceStatus.SERVICING.getType())
                .last("limit 1");
        DtrdRlPackagePatient packagePatient = packagePatientRlService.getOne(wrapper);
        if (packagePatient != null) {
            LambdaQueryWrapper<DtrdRlServicePackage> wrapper2 = Wrappers.lambdaQuery(DtrdRlServicePackage.class)
                    .eq(DtrdRlServicePackage::getPackageId, packageId);
            List<DtrdRlServicePackage> servicePackages = servicePackageRlService.list(wrapper2);
            List<Integer> ids = servicePackages.stream()
                    .filter(DtrdRlServicePackage::isFlag)
                    .map(DtrdRlServicePackage::getServiceId)
                    .collect(Collectors.toList());
            return ids.containsAll(serviceIds);
        }
        return true;
    }

    @Override
    public boolean updateServiceItemState(Integer serviceItemId, String remark, Integer type, String username) {
        // 设置完成状态和备注
        DtrdRlPatientServiceDetail serviceDetail = patientServiceDetailRlService.getById(serviceItemId);
        if (serviceDetail == null) {
            return false;
        }
        serviceDetail.setStatus(type);
        if (StrUtil.isNotBlank(remark)) {
            serviceDetail.setRemark(remark);
        }
        serviceDetail.setExecutor(username);
        // 完成时间
        serviceDetail.setFinishedTime(new Date());
        sendServiceItemStateChangeEvent(serviceDetail.getPatientId(), serviceDetail.getServiceName(), serviceDetail.getRelationId());
        return patientServiceDetailRlService.updateById(serviceDetail);
    }

    @Override
    public List<DtrdRlPackagePatient> getServicePackageDuration(String patientIds) {
        return baseMapper.getServicePackageDuration(patientIds);
    }

    @Override
    public boolean finishedServicePackage(@NotNull DtrdRlPackagePatient po, String servicePackageId, String executorId, String executorName, String serviceEffect, String serviceEndDesc) {
        Integer packageServiceStatus = Optional.ofNullable(po.getStatus()).orElse(0);
        if (PackageServiceStatus.isServiceEnd(packageServiceStatus)) {
            // 已结束无须重复处理
            return true;
        }
        po.setManageEffect(serviceEffect);
        po.setExecutorId(executorId);
        po.setExecutorName(executorName);
        po.setFinishTime(new Date());
        String remark;
        if (packageServiceStatus == PackageServiceStatus.SERVICING.getType()) {
            // region获取首诊报告，如果没有则生成首诊并生成总结报告
            ReportFirst firstReport = reportService.getPatientFirstReport(po.getPatientId(), Constant.ReportType.REPORT_FIRST.getType());
            if (firstReport == null) {
                reportService.createReportFirstV2(po.getPatientId(), executorName);
            }
            reportService.createReportSummary(po.getPatientId(), executorName);
            // endregion 判断是否生成首诊报告，并生成总结报告
            packageServiceStatus = PackageServiceStatus.QUIT.getType();
            remark = "退出服务";
        } else {
            packageServiceStatus = PackageServiceStatus.FINISHED.getType();
            remark = "结束服务";
        }
        signinPlanService.finishPatientSignInPlan(po.getPatientId(), po.getStartTime(), po.getEndTime(), remark);
        po.setStatus(packageServiceStatus);
        po.setServiceEndSummary(serviceEndDesc);
        return saveOrUpdate(po);
    }

    /**
     * 给医生和患者分别发送对应模板消息
     *
     * @param patientId
     * @param serviceName
     * @param packageId
     */
    private void sendServiceItemStateChangeEvent(Integer patientId, String serviceName, Integer packageId) {
        msgEventPusher.sendServiceItemStateChangeEvent(patientId, serviceName, packageId, false);
    }

    @Override
    public List<DtrdRlPackagePatient> getTimePeriodConflict(Integer patientId, Date serviceStartTime, Date endTime) {
        return baseMapper.getServicePackageByTime(patientId, serviceStartTime, endTime);
    }

    /**
     * @param patientId
     * @param serviceStartTime
     * @param endTime
     * @return null 即校验通过 否则返回校验不通过原因
     */
    @Override
    public String checkServicePackageValid(Integer patientId, Date serviceStartTime, Date endTime) {
        // 新增服务中服务包检测  开始时间<=今天 即服务中服务包  肖 2022-06-30 18:20:26
        if (DateUtils.isPast(serviceStartTime)) {
            // 已开始服务包
            DtrdRlPackagePatient patientPackage = packagePatientRlService.getServicingPatientPackage(patientId);
            if (patientPackage != null) {
                log.info("DtrdEntRdArchiveReverseSchemeServiceImpl.saveReverseScheme.[dto] 已存在服务中服务包");
                return "已存在服务中服务包";
            }
        }
        List<DtrdRlPackagePatient> list = packagePatientRlService.getTimePeriodConflict(patientId, serviceStartTime, endTime);
        if (CollectionUtil.isNotEmpty(list)) {
            log.info("DtrdEntRdArchiveReverseSchemeServiceImpl.saveReverseScheme.[dto] 存在时间覆盖冲突的服务包");
            return "服务周期冲突，请重新选择服务周期";
        }
        return null;
    }

    @Override
    public List<Integer> getServicingPatientIds() {
        return baseMapper.getValidPatientIds(PackageServiceStatus.SERVICING.getType());
    }

    @Override
    public PatientServiceProgramme getPatientServiceProgramme(Integer patientId) {
        return baseMapper.getPatientServiceProgramme(patientId);
    }

    private LambdaQueryWrapper<DtrdRlPackagePatient> baseQueryWrapper() {
        return Wrappers.lambdaQuery(DtrdRlPackagePatient.class)
                .eq(DtrdRlPackagePatient::getIsDel, 0);
    }

    private LambdaUpdateWrapper<DtrdRlPackagePatient> baseUpdateWrapper() {
        return Wrappers.lambdaUpdate(DtrdRlPackagePatient.class)
                .eq(DtrdRlPackagePatient::getIsDel, 0);
    }
}
